package com.link.gateway.core.filter.global;

import com.link.gateway.base.urlinfo.model.UrlInfo;
import com.link.gateway.core.cllog.LCLogger;
import com.link.gateway.core.comproperty.*;
import com.link.gateway.core.utils.CacheUtil;
import com.link.gateway.core.utils.FunctionTimeUtil;
import com.link.gateway.core.utils.RequestDataUtils;
import com.link.gateway.core.utils.ResponseDataUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ExecutionException;


/**
 * @author guoshan
 * @date 2019/08/05
 * 获取请求体，主要是为了解决其他filter中获取不到body的问题。
 */
@Component
public class CacheBodyUrlAttrGatewayFilter implements Ordered, GlobalFilter {


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //跳过检测
        Object ignoreGlobalFilter = exchange.getAttribute(OtherConstants.ATTRIBUTE_IGNORE_GLOBAL_FILTER);
        if (ignoreGlobalFilter != null && (boolean)ignoreGlobalFilter) {
            return chain.filter(exchange);
        }

        //将是否记录URL访问参数和响应体缓存到exchange中
        Map<String, UrlInfo> mapUrlDetail = null;
        try {
            mapUrlDetail = CacheUtil.getAllUrlDetailFromCache(TokenConstants.ALL_URL_DETAIL_CACHE_KEY);
        } catch (ExecutionException e) {
            LCLogger.withException(e.getMessage());
            return ResponseDataUtils.writeWith(InterfaceLogConstants.EXCEPTION, "从缓存中获取URL异常！", "Y", exchange, null);
        }
        ServerHttpRequest request = exchange.getRequest();
        //获取得到当前的url
        String requestUrl = request.getURI().getPath();
        UrlInfo urlInfo = mapUrlDetail.get(requestUrl);

        //缓存URL
        exchange.getAttributes().put(ReqAndRespConstants.REQUEST_URL,requestUrl);
        if (urlInfo == null) {
            return  chain.filter(exchange);
        }
        String outParaFlag = urlInfo.getOutParaFlag();
        String inputParaFlag = urlInfo.getInputParaFlag();
//        判断是否需要记录响应内容，如果需要将标识存入exchange中
        if ("Y".equalsIgnoreCase(outParaFlag)) {
            exchange.getAttributes().put(ReqAndRespConstants.RECORD_RESPONSE_BODY_FLAG,"Y");
        }

        if ("Y".equalsIgnoreCase(inputParaFlag)) {
            exchange.getAttributes().put(ReqAndRespConstants.RECORD_REQUEST_BODY_FLAG,"Y");
        }
        //如果不需要记录请求体则直接放行
        if ("N".equalsIgnoreCase(inputParaFlag)) {
            return chain.filter(exchange);
        }

        MediaType contentType = exchange.getRequest().getHeaders().getContentType();

        if ( contentType == null || contentType.toString().startsWith("multipart/form-data")) {
            return chain.filter(exchange);
        } else {
            return cacheReqBody(exchange,chain);
        }

    }


    private Mono<Void> cacheReqBody(ServerWebExchange exchange, GatewayFilterChain chain) {
        long begin = System.currentTimeMillis();
        Mono<DataBuffer> bufferMono = DataBufferUtils.join(exchange.getRequest().getBody());
        return bufferMono.flatMap(dataBuffer -> Mono.just(Optional.of(dataBuffer))).defaultIfEmpty(Optional.empty())
                //如果 dataBuffer传入为空，则调用 flatMap 不会走到业务节点内部，直接返回了，
                //所以需要通过.defaultIfEmpty(Optional.empty()) 构造一个值进去
                .flatMap(option -> {
                    if (!option.isPresent()) {
                        //判断 dataBuffer 是否为空，如果为空，则走该逻辑
                        ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(
                                exchange.getRequest()) {
                            @Override
                            public Flux<DataBuffer> getBody() {
                                //往序列里面放一个东西进去，确保能往下调用
                                return Flux.just();
                            }
                        };
                        return chain.filter(exchange.mutate().request(mutatedRequest).build());
                    }

                    DataBuffer d = option.get();
                    DataBufferUtils.retain(d);
                    Flux<DataBuffer> cachedFlux = Flux
                            .defer(() -> Flux.just(d.slice(0, d.readableByteCount())));
                    ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(
                            exchange.getRequest()) {
                        @Override
                        public Flux<DataBuffer> getBody() {
                            return cachedFlux;
                        }

                    };
                    String reqBody = RequestDataUtils.toRaw(cachedFlux);
                    exchange.getAttributes().put(ReqAndRespConstants.CACHE_REQUEST_BODY_KEY, reqBody);

                    FunctionTimeUtil.functionTime("CacheBodyGatewayFilterPre", "CacheBodyGatewayFilter", begin, System.currentTimeMillis(), false);
                    return chain.filter(exchange.mutate().request(mutatedRequest).build()).then(Mono.fromRunnable(() -> {
                        long end = System.currentTimeMillis();
                        FunctionTimeUtil.functionTime("CacheBodyGatewayFilterPost", "CacheBodyGatewayFilter", begin, end, false);
                    }));
                });
    }

    @Override
    public int getOrder() {
        return FilterOrderConstants.CACHE_REQ_BODY;
    }
}
